package se.kayarr.cardplay.gameplay;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import lombok.Getter;
import se.kayarr.cardplay.exceptions.SessionInProgressException;
import se.kayarr.cardplay.exceptions.UnsetVariableException;

public class CardManager extends SimpleFileVisitor<Path> {
	private static final CardManager instance = new CardManager();
	
	public static CardManager getInstance() {
		return instance;
	}
	
	private static int lastNumericId = 0;
	
	private int generateNumericId() {
		return ++lastNumericId;
	}
	
	private void resetNumericIdCounter() {
		lastNumericId = 0;
	}
	
	private Map<String, Class<? extends Card>> cardTypeMap = new HashMap<>();
	
	private List<Card> cards = new ArrayList<>();
	
	public List<Card> getCards() {
		return Collections.unmodifiableList(cards);
	}
	
	public Card findCardById(String id) {
		for(Card card : cards) {
			if(card.getDisplayId().equalsIgnoreCase(id)) return card;
		}
		return null;
	}
	
	public Card findCardByRefId(String refId) {
		for(Card card : cards) {
			if(card.getRefId().equals(refId)) return card;
		}
		return null;
	}
	
	@SuppressWarnings("unchecked")
	public <T extends Card> T findCardByCardType(int index, Class<T> type) {
		for(Card card : cards) {
			if(type.isInstance(card) && index-- == 0) return (T) card;
		}
		return null;
	}
	
	public <T extends Card> int findCardAmountByCardType(Class<T> type) {
		int amnt = 0;
		for(Card card : cards) if(type.isInstance(card)) amnt++;
		return amnt;
	}
	
	public List<Card.Instance> generateDeck(Session session, Player owner) {
		List<Card.Instance> returnList = new ArrayList<>();
		Random random = new Random();
		
		//TODO Make deck generating more interesting
		for(int i = 0; i < 60; i++) {
			returnList.add( cards.get( random.nextInt(cards.size()) ).newInstance(session, owner) );
		}
		
		return returnList;
	}
	
	private CardManager() {
		cardTypeMap.put("unit", UnitCard.class);
		//cardTypeMap.put("tool", ToolCard.class);
		cardTypeMap.put("action", ActionCard.class);
		
		try {
			loadCards(Paths.get("./cards"));
		}
		catch (SessionInProgressException e) {}
	}
	
	public void reloadCards() throws SessionInProgressException {
		Session.checkSessionInProgress();
		
		loadCards(Paths.get("./cards"));
	}
	
	public void loadCards(File folder) throws SessionInProgressException {
		loadCards(folder.toPath());
	}
	
	public void loadCards(Path folder) throws SessionInProgressException {
		Session.checkSessionInProgress();
		
		System.out.println("Begin loading cards...");
		
		long beginTime = System.nanoTime();
		
		try {
			cards.clear();
			resetNumericIdCounter();
			exceptionCountLastLoad = 0;
			
			Files.walkFileTree(folder, this);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		long endTime = System.nanoTime();
		
		double ms = (endTime - beginTime) / 1000000d;
		
		System.out.format("Loaded %d cards (%d failed to load), " +
				"took %.2f ms%n", cards.size(), exceptionCountLastLoad, ms);
	}
	
	private final Pattern jsFileRegex = Pattern.compile("^(\\w+)[-_].+?\\.js$");
	
	@Getter private int exceptionCountLastLoad = 0;
	
	@Override
	public FileVisitResult visitFile(Path file, BasicFileAttributes attr) throws IOException {
		try {
			Session.checkSessionInProgress();
			
			Matcher matcher = jsFileRegex.matcher(file.getFileName().toString());
			if(matcher.find()) {
				String cardType = matcher.group(1).toLowerCase();
				if(cardTypeMap.containsKey(cardType)) {
					try {
						Card cardData = cardTypeMap.get(cardType)
								.getConstructor(int.class).newInstance(generateNumericId());
						
						cardData.loadScript(file);
						
						cards.add(cardData);
					}
					catch(UnsetVariableException e) {
						System.out.println("The required variable \"" + e.getVarName() +
								"\" was left unset for file " + file + "!");
						exceptionCountLastLoad++;
					}
					catch (Exception e) {
						e.printStackTrace();
						exceptionCountLastLoad++;
					}
				}
				else System.out.println("Unknown card type \"" + cardType + "\" for file " + file);
			}
			
			return FileVisitResult.CONTINUE;
		}
		catch(SessionInProgressException e) {
			return FileVisitResult.CONTINUE;
		}
	}
}
